﻿using DA6502.Controls;
using System.Collections.Generic;

// 此文件中定义CPU的硬件部件以及其访问方法
namespace DA6502.CPU
{
    using addr = System.UInt16;

    /// <summary>
    /// 6502 CPU 模拟
    /// </summary>
    public partial class CPU
    {
        //  6502 CPU 中断向量表
        public const addr INTERRUPT_VECTOR_NMI = 0xFFFA;
        public const addr INTERRUPT_VECTOR_RST = 0xFFFC;
        public const addr INTERRUPT_VECTOR_IRQ = 0xFFFE;

        //  RegPanel（显示寄存器状态的控件）中用户改变寄存器的值是会调用委托将变化反映到CPU寄存器里。
        //  同时CPU寄存器状态变化后其值也会通过委托反映到RegPanel中。
        //  为了防止循环调用，这里设置了一个开关。
        public bool CallbackEnable = true;

        //  后来发现这里是不是改成用ENUM更省事...
        /// <summary>
        /// CPU状态寄存器标志位掩码
        /// </summary>
        public static Dictionary<FLAGS, byte> FLAG_MASK = new Dictionary<FLAGS, byte>
        {
            { FLAGS.C, 0b00000001 },
            { FLAGS.Z, 0b00000010 },
            { FLAGS.I, 0b00000100 },
            { FLAGS.D, 0b00001000 },
            { FLAGS.B, 0b00010000 },
            { FLAGS.U, 0b00100000 },
            { FLAGS.V, 0b01000000 },
            { FLAGS.N, 0b10000000 }
        };

        #region ==============================| 6502 CPU 的寄存器 及 CPU状态标志位 |==============================

        /// <summary>
        /// 程序寄存器 Programme Counter
        /// </summary>
        protected addr PC
        {
            get => _pc;
            set
            {
                _pc = value;
                if (OnPcChange != null && CallbackEnable) OnPcChange(value);
            }
        }
        private addr _pc;

        /// <summary>
        /// 累加器A Accumulator
        /// </summary>
        protected byte A
        {
            get => _a;
            set
            {
                _a = value;
                if (OnRegisterChange != null && CallbackEnable) OnRegisterChange(REGISTER_BYTE.A, value);
            }
        }
        private byte _a;

        /// <summary>
        /// 索引寄存器X
        /// </summary>
        protected byte X
        {
            get => _x;
            set
            {
                _x = value;
                if (OnRegisterChange != null && CallbackEnable) OnRegisterChange(REGISTER_BYTE.X, value);
            }
        }
        private byte _x;

        /// <summary>
        /// 索引寄存器Y
        /// </summary>
        protected byte Y
        {
            get => _y;
            set
            {
                _y = value;
                if (OnRegisterChange != null && CallbackEnable) OnRegisterChange(REGISTER_BYTE.Y, value);
            }
        }
        private byte _y;

        /// <summary>
        /// 处理状态器P Status Register
        /// </summary>
        protected byte P
        {
            get => _p;
            set
            {
                _p = value;
                if (OnRegisterChange != null && CallbackEnable) OnRegisterChange(REGISTER_BYTE.P, value);
            }
        }
        private byte _p;

        /// <summary>
        /// 栈指针SP Stack Pointer
        /// </summary>
        protected byte S
        {
            get => _s;
            set
            {
                _s = value;
                if (OnRegisterChange != null && CallbackEnable) OnRegisterChange(REGISTER_BYTE.S, value);
            }
        }
        private byte _s;

        /// <summary>
        /// 进位标志
        /// </summary>
        protected bool C { get => GetFlag(FLAGS.C); set => SetFlag(FLAGS.C, value); }

        /// <summary>
        /// 零标志
        /// </summary>
        protected bool Z { get => GetFlag(FLAGS.Z); set => SetFlag(FLAGS.Z, value); }

        /// <summary>
        /// 中断禁用标志
        /// </summary>
        protected bool I { get => GetFlag(FLAGS.I); set => SetFlag(FLAGS.I, value); }

        /// <summary>
        /// 十进制模式标志
        /// </summary>
        protected bool D { get => GetFlag(FLAGS.D); set => SetFlag(FLAGS.D, value); }

        /// <summary>
        /// Break命令标志
        /// </summary>
        protected bool B { get => GetFlag(FLAGS.B); set => SetFlag(FLAGS.B, value); }

        /// <summary>
        /// 未使用标志
        /// </summary>
        protected bool U { get => GetFlag(FLAGS.U); set => SetFlag(FLAGS.U, value); }

        /// <summary>
        /// 溢出标志
        /// </summary>
        protected bool V { get => GetFlag(FLAGS.V); set => SetFlag(FLAGS.V, value); }

        /// <summary>
        /// 负标志
        /// </summary>
        protected bool N { get => GetFlag(FLAGS.N); set => SetFlag(FLAGS.N, value); }

        /// <summary>
        /// 取CPU状态标志位
        /// </summary>
        /// <param name="flag">要取的标志位名称</param>
        /// <returns>标志位的值</returns>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        protected bool GetFlag(FLAGS flag) => (P & FLAG_MASK[flag]) != 0;

        /// <summary>
        /// 设置状态标志位为1
        /// </summary>>
        /// <param name="flag">标志位名称</param>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        protected void SetFlag(FLAGS flag) => SetFlag(flag, true);

        /// <summary>
        /// 设置状态标志位为指定值
        /// </summary>
        /// <param name="flag">标志位名称</param>
        /// <param name="value">要设置的值</param>
        protected void SetFlag(FLAGS flag, bool value)
        {
            byte mask = FLAG_MASK[flag];
            if (value) P |= mask;
            else P &= (byte)~mask;
        }

        /// <summary>
        /// 设置状态标志位为0
        /// </summary>
        /// <param name="flag">标志位名称</param>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        protected void UnsetFlag(FLAGS flag) => SetFlag(flag, false);

        /// <summary>
        /// 取得一字节长的寄存器值
        /// </summary>
        /// <param name="register">寄存器名称</param>
        /// <returns>寄存器值</returns>
        public byte GetRegisterByte(REGISTER_BYTE register)
        {
            byte result = 0;
            switch (register)
            {
                case REGISTER_BYTE.A:
                    result = A;
                    break;
                case REGISTER_BYTE.P:
                    result = P;
                    break;
                case REGISTER_BYTE.X:
                    result = X;
                    break;
                case REGISTER_BYTE.Y:
                    result = Y;
                    break;
                case REGISTER_BYTE.S:
                    result = S;
                    break;
            }
            return result;
        }

        /// <summary>
        /// 取2字节长的寄存器值（只有PC）
        /// </summary>
        /// <param name="register">寄存器名称</param>
        /// <returns>寄存器值</returns>
        public addr GetRegisterWord(REGISTER_WORD register) => PC;

        /// <summary>
        /// 设1字节长的寄存器值
        /// </summary>
        /// <param name="register">寄存器名称</param>
        /// <param name="value">要设的值</param>
        public void SetRegisterByte(REGISTER_BYTE register, byte value)
        {
            switch (register)
            {
                case REGISTER_BYTE.A:
                    A = value;
                    break;
                case REGISTER_BYTE.P:
                    P = value;
                    break;
                case REGISTER_BYTE.X:
                    X = value;
                    break;
                case REGISTER_BYTE.Y:
                    Y = value;
                    break;
                case REGISTER_BYTE.S:
                    S = value;
                    break;
            }
        }

        /// <summary>
        /// 设2字节长的寄存器的值（只有PC）
        /// </summary>
        /// <param name="register">寄存器名称</param>
        /// <param name="address">寄存器值</param>
        public void SetRegisterWord(REGISTER_WORD register, addr address) => PC = address;


        //================================================================================
        //  私有标志位，表明外部引发了CPU的某个中断。此中断将在下一个时钟同期开始的时候处理
        //================================================================================
        protected bool rstFlag = false;
        protected bool irqFlag = false;
        protected bool nmiFlag = false;

        #endregion

        /// <summary>
        /// 生成一个 6502 CPU 的实例，并指定其使用的IO总线
        /// </summary>
        /// <param name="ioBus">IO总线</param>
        public CPU(MemoryView ioBus)
        {
            IO = ioBus;
            FillCommandList();
        }

        /// <summary>
        /// CPU上电
        /// </summary>
        public void PowerUp()
        {

            //  P = $34 (IRQ disabled)
            //  大约 1/60 秒后，APU将IRQ使能(NES的功能，在反汇编中也没啥影响)
            P = 0x24;

            //  A, X, Y = 0
            A = 0;
            X = 0;
            Y = 0;

            S = 0x00;

            Reset();
        }

        /// <summary>
        /// 取指令/取操作数。
        /// 每次从IO中PC指向的地址读取一个字节，并将PC+1
        /// </summary>
        /// <returns>取得的指令或者操作数</returns>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        protected byte IF() => IO[PC++];

        #region ==============================| 出入栈 |==============================

        /// <summary>
        /// 压 1 个字节入栈，同时栈指针 S - 1
        /// </summary>
        /// <param name="value">入栈字节</param>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        protected void PushByte(byte value)
        {
            IO[(addr)(S | 0x0100)] = value;
            S--;
        }

        /// <summary>
        /// 压 2 个字节入栈，高先低后，同时栈指针 S - 2
        /// </summary>
        /// <param name="address">入栈值</param>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        protected void PushAddr(addr address)
        {
            PushByte(AddrHigh(address));
            PushByte(AddrLow(address));
        }

        /// <summary>
        /// 弹 1 个字节出栈，同时栈指针 S + 1
        /// </summary>
        /// <returns>出栈字节</returns>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        protected byte PopByte()
        {
            S++;
            byte val = IO[(addr)(S | 0x0100)];
            return val;
        }

        /// <summary>
        /// 弹 2 个字节出栈，低先高后，同时栈指针 S + 2
        /// </summary>
        /// <returns>出栈值</returns>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        protected addr PopAddr()
        {
            byte low = PopByte();
            byte high = PopByte();
            return AddrJoin(high, low);
        }

        #endregion

        #region ==============================| 中断 |==============================

        /// <summary>
        /// IRQ中断
        /// </summary>
        public void Irq() => irqFlag = true;

        /// <summary>
        /// NMI中断
        /// </summary>
        public void Nmi() => nmiFlag = true;

        /// <summary>
        /// RESET中断
        /// </summary>
        public void Reset() => rstFlag = true;

        /// <summary>
        /// IRQ中断处理
        /// </summary>
        protected void OnInterruptIRQ()
        {
            I = true;
            B = false;
            U = true;
            PushAddr(PC);
            PushByte(P);
            irqFlag = false;

            Execute(0x4C, INTERRUPT_VECTOR_IRQ);  //  JMP (INTERRUPT_VECTOR_IRQ)
        }

        /// <summary>
        /// NMI中断处理
        /// </summary>
        protected void OnInterruptNMI()
        {
            I = true;
            B = false;
            U = true;
            PushAddr(PC);
            PushByte(P);
            nmiFlag = false;

            Execute(0x4C, INTERRUPT_VECTOR_NMI);  //  JMP (INTERRUPT_VECTOR_NMI)
        }

        /// <summary>
        /// RST中断处理
        /// </summary>
        protected void OnInterruptReset()
        {
            //  A, X, Y were not affected
            //  S was decremented by 3(but nothing was written to the stack)[3]
            //  S减3是因为RESET与NMI、IRQ及BRK共享一部分代码，这部分代码会压PC和P入栈。
            //  这里模拟时不需要

            //  The I(IRQ disable) flag was set to true(status ORed with $04)
            I = true;
            S -= 3;

            nmiFlag = false;
            kilFlag = false;
            rstFlag = false;

            //  开机执行Reset中断，跳转至 INTERRUPT_VECTOR_RST 中保存的入口地址开始执行
            Execute(0x4C, INTERRUPT_VECTOR_RST);  //  JMP (INTERRUPT_VECTOR_RST)
        }

        #endregion

        #region ==============================| Utils |==============================

        /// <summary>
        /// 判断当前CPU字节序是否为小端(Little Endian)
        /// </summary>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static bool IsLittleEndian() => ((short)0x0001 & (byte)0x01) > 0;

        /// <summary>
        /// 判断当前CPU字节序是否为大端(Little Endian)
        /// </summary>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static bool IsBigEndian() => !IsLittleEndian();

        /// <summary>
        /// 将两个字节按CPU字节序拼接成一个双字节地址
        /// </summary>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static addr AddrJoin(byte h, byte l) => (addr)(IsLittleEndian() ? ((h << 8) | l) : ((l << 8) | h));

        /// <summary>
        /// 从一个双字节地址中按CPU字节序取出高8位
        /// </summary>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static byte AddrHigh(addr ad) => (byte)(IsLittleEndian() ? (ad & 0xFF00) >> 8 : (ad & 0x00FF));

        /// <summary>
        /// 从一个双字节地址中按CPU字节序取出低8位
        /// </summary>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static byte AddrLow(addr ad) => (byte)(IsLittleEndian() ? (ad & 0x00FF) : (ad & 0xFF00) >> 8);

        /// <summary>
        /// 判断一个字节做为有符号字节型时是否为负数（最高位是否为1）
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static bool IsNegative(byte v) => (v & 0x80) > 0;

        #endregion

    }
}